<?php

namespace StudyBuddy\Controllers;

use \StudyBuddy\Responder;
use \StudyBuddy\WebPage;
use \StudyBuddy\Request;


/**
 * Controller for processing a vote
 * for question or answer
 *
 * @todo require post and token
 * send via Ajax by post only!
 * Otherwise it's too easy to fake a vote
 * by simple script tag on external website
 * at the very least require a token and validate it!
 * This will work even with get request
 *
 */
class Vote extends WebPage
{

	protected $membersOnly = true;

	protected $permission = 'vote';

	protected $aRequired = array('resid', 'type', 'res');

	protected $resID;

	/**
	 * Resource receiving a vote
	 * This could be a object of Question or Answer class
	 *
	 * @var object
	 */
	protected $oResource;

	/**
	 * What is the vote for: QUESTION or ANSWER?
	 *
	 * @var string
	 */
	protected $resType = 'QUESTION';

	/**
	 * Type of vote (up or down)
	 *
	 * @var string can be 'up' or 'down'
	 */
	protected $voteType;


	/**
	 * By how much to increment the up/down vote
	 * this is either 1 or -1
	 *
	 * @var int
	 */
	protected $inc = 1;



	protected function main(){
		$this->oRegistry->registerObservers('INPUT_FILTERS');
		$this->resID = (int)$this->oRequest['resid'];
		$this->voteType = $this->oRequest['type'];
		$this->resType = ('a' === $this->oRequest['res']) ? 'ANSWERS' : 'QUESTIONS';
		
		if(!in_array($this->voteType, array('up', 'down'))){
			throw new \StudyBuddy\Exception('Invalid type of vote');
		}

		try{
			$this->getResource()
			->postBeforeEvent()
			->checkIsOwner()
			->getIncrementValue()
			->increaseVoteCount()
			->updateQuestion()
			->setOwnerReputation()
			->postEvent();
		} catch (\Exception $e){
			d('Vote not counted due to exception: '.$e->getMessage().' in '.$e->getFile().' line: '.$e->getLine());
		}

		$this->handleReturn();
	}


	/**
	 *
	 * Post onBeforeVote event
	 * and allow observer to cancel event
	 *
	 * @return object $this
	 */
	protected function postBeforeEvent(){
		$notification  = $this->oRegistry->Dispatcher->post($this->oResource, 'onBeforeVote', array('type' => $this->voteType ));
		if($notification->isNotificationCancelled()){
			throw new \StudyBuddy\Exception('Cancelled onBeforeVote event');
		}

		return $this;
	}


	/**
	 *
	 * Post onNewVote event
	 *
	 * @return object $this
	 */
	protected function postEvent(){

		$this->oRegistry->Dispatcher->post($this->oResource, 'onNewVote', array('type' => $this->voteType, 'isUndo' => (1 === $this->inc) ));

		return $this;
	}


	/**
	 * Instantiate the resource object
	 *
	 * @return object $this
	 */
	protected function getResource(){
		$a = $this->oRegistry->Mongo->getCollection($this->resType)
		->findOne(array('_id' => $this->resID));

		if(empty($a) || empty($a['_id'])){
			throw new \StudyBuddy\Exception('Invalid question or answer id '.$this->resID);
		}

		$class = ('QUESTIONS' === $this->resType) ? '\\StudyBuddy\\Question' : '\\StudyBuddy\\Answer';

		$this->oResource = new $class($this->oRegistry, $a);
		d('$this->oResource: '.$this->oResource->getClass());

		return $this;
	}


	/**
	 * Check if viewer is owner of resource
	 * users not allowed to vote for their own
	 * question
	 *
	 * @return object $this
	 */
	protected function checkIsOwner(){

		if(\StudyBuddy\isOwner($this->oRegistry->Viewer, $this->oResource)){
			throw new \StudyBuddy\Exception('Cannot rate own questions or answers');
		}

		return $this;
	}


	/**
	 * Get the value of increment
	 * this will be either 1 or -1 in case of
	 * undo a vote
	 *
	 * @return object $this
	 */
	protected function getIncrementValue(){

		$coll = $this->oRegistry->Mongo->getCollection('VOTES');
		$coll->ensureIndex(array('i_uid' => 1));
		$coll->ensureIndex(array('i_res' => 1));


		$uid = $this->oRegistry->Viewer->getUid();
		$aData = array(
		'i_uid' => $uid,
		'i_res' => $this->resID,
		't' => $this->voteType);

		$aRes = $coll->findOne($aData);

		d('$aRes: '.print_r($aRes, 1));

		/**
		 * If record exists, then this is an 'unvote'
		 * in which case we must delete the record
		 * and return
		 */
		if($aRes && !empty($aRes['_id'])){
			$this->inc = -1;
			$coll->remove(array('_id' => $aRes['_id']));
		} else {
			$aData['i_ts'] = time();
			$aData['i_owner'] = $this->oResource->getOwnerId();
			$coll->insert($aData);
		}

		return $this;
	}


	/**
	 * Increase or decrease owner's reputation
	 * after his question or answer receives a vote
	 *
	 * @return object $this
	 */
	protected function setOwnerReputation(){

		$uid = $this->oResource->getOwnerId();
		d('uid of resource owner: '.$uid);
		/**
		 * Now need to calculate points
		 *
		 */
		try{
			\StudyBuddy\User::factory($this->oRegistry)->by_id($uid)->setReputation($this->calculatePoints());
		} catch(\Exception $e){
			e($e->getMessage().' in file: '.$e->getFile().' on line: '.$e->getLine());
		}
		
		return $this;
	}
	
	/**
	 *
	 * Calculate points
	 * This would depend on
	 * Upvote/Downvote
	 * Is Undo
	 * Resource type: Q or A
	 *
	 * @return int
	 */
	protected function calculatePoints(){
		if('down' === $this->voteType){
			$points = \StudyBuddy\Points::DOWNVOTE;
		} elseif('QUESTION' === $this->resType){
			$points = \StudyBuddy\Points::UPVOTE_Q;
		} else {
			$points = \StudyBuddy\Points::UPVOTE_A;
		}

		$points = ($this->inc * $points);
		d('$points: '.$points);

		return $points;
	}


	/**
	 * Increment value of i_up/i_down of question/answer
	 *
	 * @return object $this
	 */
	protected function increaseVoteCount(){
		if('up' === $this->voteType){
			$this->oResource->addUpVote($this->inc)->touch(true);
		} else {
			$this->oResource->addDownVote($this->inc)->touch(true);
		}

		return $this;
	}


	/**
	 * If vote was on the ANSWER
	 * we still need to update i_lm_ts of question
	 * to prevent browser displaying cached version
	 *
	 * @return object $this
	 */
	protected function updateQuestion(){
		if('ANSWERS' === $this->resType){
			try{
				$this->oRegistry->Mongo->QUESTIONS
				->update(
				array('_id' => $this->oResource['i_qid']),
				array('$set' =>
				array('i_etag' => time())));

			} catch (\Exception $e){
				e('unable to update question after vote for answer is received '.$this->oResource['i_qid']);
			}
		}

		if($this->resType == 'ANSWERS'){
			$actionCount = 'answer_voted_total';
		}else{
			$actionCount = 'question_voted_total';
		}
		$this->makeActionCount($actionCount, $this->oResource->getOwnerId());
		$this->makeActionCount('vote_total', $this->oRegistry->Viewer->getUid());
		
		$this->checkGainOtherBadges();
		
		return $this;
	}

	//hoangto make action count
	function makeActionCount($countType = '', $uid = 0){
	
		//$uid = $this->oResource->getOwnerId();
	
		$coll = $this->oRegistry->Mongo->ACTIONCOUNT;
	
		$count = $coll->findOne(array('i_uid' => $uid), array('_id', $countType));
		
		if(empty($count[$countType])){
			$total = 1;
		}else{
			$total = (int)$count[$countType] + 1;
		}
			
		if(empty($count['_id'])){
			$countData = array(
				'i_uid' => $uid,
				$countType => $total
			);
			$this->oRegistry->Mongo->ACTIONCOUNT->insert($countData);
		}else{
			$countData = array(
				'$set' => array($countType => $total)
			);
			$this->oRegistry->Mongo->ACTIONCOUNT->update(array('i_uid' => $uid), $countData);
		}
		
		$coll->ensureIndex(array('i_uid' => 1));
		
		$this->checkGainBadges($countType, $uid);
		
		return $this;
	}
	
	//hoangto
	protected function checkGainBadges($countType, $uid){
		
		$count = $this->oRegistry->Mongo->ACTIONCOUNT->findOne(array('i_uid' => $uid), array($countType));
		$cond = $this->oRegistry->Mongo->BADGES->find(array('cond_type' => $countType, 'cond' => array('$lte' => $count[$countType])));
		
		foreach($cond as $obj){
			$actData = array(
				'i_uid' => $uid,
				'username' => $this->oResource['username'],
				'i_bid' => $obj['id'],
				'badges' => $obj['b_name'],
				'badges_type' => $obj['type']
			);
			$isExist = $this->oRegistry->Mongo->USER_BADGES->findOne($actData);
			if(empty($isExist)){
				$this->oRegistry->Mongo->USER_BADGES->insert($actData);
				$this->makeActivities('badges', 'earned a badges', $obj['id'], $uid);
			}
			$this->oRegistry->Mongo->USER_BADGES->ensureIndex(array('i_uid' => 1));
			$this->oRegistry->Mongo->USER_BADGES->ensureIndex(array('badges' => 1));
		}
		
		return $this;
	}
	
	//hoangto
	protected function checkGainOtherBadges(){
		
		if($this->resType == 'ANSWERS'){
			$a = $this->oRegistry->Mongo->ANSWERS->findOne(array('_id' => $this->resID));
			$cond = $this->oRegistry->Mongo->BADGES->find(array('cond_type' => 'a_answer_voted_over', 'cond' => array('$lte' => $a['i_votes'])));
		}else{
			$a = $this->oRegistry->Mongo->QUESTIONS->findOne(array('_id' => $this->resID));
			$cond = $this->oRegistry->Mongo->BADGES->find(array('cond_type' => 'a_question_voted_over', 'cond' => array('$lte' => $a['i_votes'])));
		}
		
		foreach($cond as $obj){
			$actData = array(
				'i_uid' => $a['i_uid'],
				'username' => $a['username'],
				'i_bid' => $obj['id'],
				'badges' => $obj['b_name'],
				'badges_type' => $obj['type']
			);
			$isExist = $this->oRegistry->Mongo->USER_BADGES->findOne($actData);
			if(empty($isExist)){
				$this->oRegistry->Mongo->USER_BADGES->insert($actData);
				$this->makeActivities('badges', 'earned a badges', $obj['id'], $a['i_uid']);
			}
			$this->oRegistry->Mongo->USER_BADGES->ensureIndex(array('i_uid' => 1));
			$this->oRegistry->Mongo->USER_BADGES->ensureIndex(array('badges' => 1));
		}
		
		return $this;
	}
	
	
	//hoangto make answer activity
	protected function makeActivities($act_type = 'a', $act_name = 'answered a question', $act_id, $uid) {
		$actData = array(
			'i_uid' => $uid,
			'username' => $this->oResource['username'],
			'avt' => $this->oResource['avtr'],
			'act_type' => $act_type,
			'act_id' => $act_id,
			'act_name' => $act_name,
			'i_ts' => time(),
			'hts' => date('F j, Y g:i a T')
		);
	
		$coll = $this->oRegistry->Mongo->ACTIVITIES;
		$coll->ensureIndex(array('i_uid' => 1));
		$coll->ensureIndex(array('i_ts' => 1));
		
		$oActivity = \StudyBuddy\MongoDoc::factory($this->oRegistry, 'ACTIVITIES', $actData);
		$res = $oActivity->save($actData);
		
		return $this;
	}

	protected function handleReturn(){
		$isAjax = Request::isAjax();
		d('$isAjax: '.$isAjax);

		if($isAjax){
			$ret = array(
			'vote' => array(
				'v' => $this->oResource->getScore(), 
				't' => $this->resType, 
				'rid' => $this->resID)
			);

			Responder::sendJSON($ret);

		}

		Responder::redirectToPage($this->oResource->getUrl());

	}
}
